package com.ls.utils;

import cn.dev33.satoken.exception.NotLoginException;
import com.ls.constant.SystemConstants;
import com.ls.exception.LoginOutOfDateException;
import com.ls.exception.MissTokenException;
import com.ls.exception.SysException;
import com.ls.exception.SysIllegalParameterException;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName GlobalExceptionAdvisor
 * @Description 全局异常处理
 * @Author TangQuanBin
 * @Date 2019/9/14 10:33
 * @Version 1.0
 * TODO 需要捕获到登录时的token过期以及 没有token的异常，返回文字给到前端
 **/
@Slf4j
@ControllerAdvice
public class GlobalExceptionAdvisor {
    
    
    @Value("${spring.servlet.multipart.max-file-size}")
    private String uploadZize;

    /**
     * 捕获@Validate校验抛出的异常（BindException）
     *
     * @param e       BindException
     * @param request HttpServletRequest
     * @return ResultData
     */
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public ResultData validExceptionHandler(BindException e, HttpServletRequest request) {
        SysException ex = parseBindingResult(e.getBindingResult());
        log.error("请求地址捕获@Validate校验抛出的异常: {} . 异常信息: {}", request.getServletPath(), ex.getMessage());
        return ResultData.failure(SystemConstants.PARAM_INCORRECT_CODE, e.getBindingResult().getFieldError().getDefaultMessage());
    }

    @ExceptionHandler(value = NotLoginException.class)
    @ResponseBody
    public ResultData handleNotLoginException(NotLoginException e) {
        log.error("测试返回");
        // 这里可以返回自定义的错误信息
//        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Authorization error: " + e.getMessage());
        return ResultData.failure(SystemConstants.TOKEN_FAIL_CODE, "Authorization error: " + e.getMessage());
    }

//    @ExceptionHandler(value = NotLoginException.class)
//    @ResponseBody
//    public ResponseEntity<String> handleNotLoginException(NotLoginException e) {
//        // 这里可以返回自定义的错误信息
//        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Authorization error: " + e.getMessage());
//    }


    /**
     * 提取Validator产生的异常错误
     *
     * @param bindingResult BindingResult
     * @return 返回异常信息
     */
    private SysException parseBindingResult(BindingResult bindingResult) {
        Map<String, String> errorMsgs = new HashMap(16);
        for (FieldError error : bindingResult.getFieldErrors()) {
            errorMsgs.put(error.getField(), error.getDefaultMessage());
        }
        if (errorMsgs.isEmpty()) {
            return new SysIllegalParameterException();
        } else {
            return new SysIllegalParameterException(JsonUtils.toJsonString(errorMsgs).replace("\"", ""));
        }
    }

    /**
     * 捕获@Validate校验抛出的异常
     *
     * @param e        MethodArgumentNotValidException异常信息
     * @param request  HttpServletRequest
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ResultData validException2Handler(MethodArgumentNotValidException e, HttpServletRequest request) {
        SysException ex = parseBindingResult(e.getBindingResult());
        log.error("请求地址捕获@Validate校验抛出的异常: {} . 异常信息: {}", request.getServletPath(), ex.getMessage());
        return ResultData.failure(SystemConstants.PARAM_INCORRECT_CODE, e.getBindingResult().getFieldError().getDefaultMessage());
    }

    /**
     * token过期异常
     *
     * @param request HttpServletRequest
     * @param e       异常信息
     * @return ResultData
     */
    @ExceptionHandler(LoginOutOfDateException.class)
    @ResponseBody
    public ResultData loginOutOfDateExceptionHandler(HttpServletRequest request, LoginOutOfDateException e) {
        log.error("请求地址token过期异常: {} . 异常信息: {}", request.getServletPath(), e.getMessage());
        return ResultData.error(e);
    }

    /**
     * 请求头中没有token异常
     *
     * @param request HttpServletRequest
     * @param e       异常信息
     * @return ResultData
     */
    @ExceptionHandler(MissTokenException.class)
    @ResponseBody
    public ResultData missingTokenExceptionHandler(HttpServletRequest request, MissTokenException e) {
        log.error("请求地址请求头中没有token异常: {} . 异常信息: {}", request.getServletPath(), e);
        return ResultData.error(e);
    }

    /**
     * 处理FengQuanException
     *
     * @param request HttpServletRequest
     * @param e       FengQuanException
     * @return ResultData
     * @throws Exception Exception
     */
    @ExceptionHandler(value = SysException.class)
    @ResponseBody
    public ResultData gisExceptionHandler(HttpServletRequest request, SysException e) {
        log.error("请求地址处理FengQuanException: {} . 异常信息: {}", request.getServletPath(), e.getMessage());
        return ResultData.error(e);
    }

    /**
     * 如果是Exception直接返回FengQuanException
     *
     * @param request HttpServletRequest
     * @param e       Exception
     * @return ResultData
     * @throws Exception Exception
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResultData exceptionHandler(HttpServletRequest request, Exception e) {
        e.printStackTrace();
        log.error("请求地址如果是Exception直接返回FengQuanException: {} . 异常信息: {}", request.getServletPath(), e.getMessage());
        return ResultData.failure("内部服务器异常");
    }

    /**
     * 文件超大提示
     *
     * @param request
     * @param e
     * @return
     * @throws Exception
     */
    @ExceptionHandler(value = MaxUploadSizeExceededException.class)
    @ResponseBody
    public ResultData maxUploadSizeExceededException(HttpServletRequest request, MaxUploadSizeExceededException e) {
        log.error("请求地址: {} . 异常信息: {}", request.getServletPath(), e.getMessage());
        return ResultData.failure("文件大小不能超过 " + uploadZize + ". 请您重新选择!");
    }

    @ExceptionHandler(value = {IllegalArgumentException.class, MethodArgumentTypeMismatchException.class, MissingServletRequestParameterException.class, HttpMessageNotReadableException.class})
    @ResponseBody
    public ResultData badArgumentHandler(RuntimeException e) {
        e.printStackTrace();
        log.error(e.getMessage(),e);
        return ResultData.failure(SystemConstants.PARAM_INCORRECT_CODE, "传参不规范");
    }

//    @ExceptionHandler(MissingServletRequestParameterException.class)
//    @ResponseStatus(HttpStatus.BAD_REQUEST)
//    @ResponseBody
//    public ResultData<String> handleMissingServletRequestParameter(MissingServletRequestParameterException e) {
//        return ResultData.failure(SystemConstants.PARAM_INCORRECT_CODE,"Required request parameter '" + e.getParameterName() + "' is not present");
//    }

    @ExceptionHandler(value = DuplicateKeyException.class)
    @ResponseBody
    public ResultData duplicateKeyHandler(DuplicateKeyException e) {
        e.printStackTrace();
        log.error(e.getMessage(),e);
        return ResultData.failure("系统繁忙请稍后重试");
    }
}
